///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include <GBuffer.h>

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
GBuffer::GBuffer()
{
	m_inited = false;
	m_bDepth = false;
	m_bStencil = false;
	m_texCount = 0;
	m_tex = NULL;
	m_mrt = NULL;
	m_width = 0;
	m_height = 0;
	m_bUseFBO = false;
}

///////////////////////////////////////////////////////////////////////////////
// destructor
///////////////////////////////////////////////////////////////////////////////
GBuffer::~GBuffer()
{
	destroy();
}


///////////////////////////////////////////////////////////////////////////////
// init buffer
///////////////////////////////////////////////////////////////////////////////
void GBuffer::init(GLuint width, GLuint height, GLuint texCount, GLboolean depth, GLboolean stencil)
{
	m_width = width;
	m_height = height;
	m_texCount = texCount;
	m_bDepth = depth;
	m_bStencil = stencil;
	m_bUseFBO = false;
}


///////////////////////////////////////////////////////////////////////////////
// create the fbo
///////////////////////////////////////////////////////////////////////////////
void GBuffer::create()
{
	destroy();
	
	ASSERT( m_bDepth || m_bStencil || m_texCount );
	ASSERT( m_width > 0 && m_width <= MAX_RES && m_height > 0 && m_height <= MAX_RES );
	
	m_bUseFBO = false;

    GLint colorBufferCount = 0;
    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &colorBufferCount);
    

	if( m_texCount )
	{
		m_tex = new GLuint[m_texCount];
		glGenTextures(m_texCount, m_tex);
		for( GLuint i = 0; i < m_texCount; i++ )
		{
			glBindTexture(GL_TEXTURE_2D, m_tex[i]);

			//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			
			//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
			//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
			//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
			//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
			
			glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap generation included in OpenGL v1.4
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
#ifndef FAST
			glBindTexture(GL_TEXTURE_2D, 0);
#endif
		}
	}
	if( m_bDepth )
	{
		glGenTextures(1, &m_texDepth);
		glBindTexture(GL_TEXTURE_2D, m_texDepth);

		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap generation included in OpenGL v1.4
	
		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
		//glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
		//glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
		//glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
	
#ifndef FAST
		glBindTexture(GL_TEXTURE_2D, 0);
#endif
	}
	if( m_bStencil )
	{
		glGenTextures(1, &m_texStencil);
		glBindTexture(GL_TEXTURE_2D, m_texStencil);

		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
		//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap generation included in OpenGL v1.4

		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

#ifndef FAST
		glBindTexture(GL_TEXTURE_2D, 0);
#endif
	}
    

	if( colorBufferCount >= (GLint) m_texCount )
	{
		m_bUseFBO = true;
		
		// create a framebuffer object, you need to delete them when program exits.
		glGenFramebuffersEXT(1, &m_fboId);
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId);

		if( m_texCount )
		{
			m_mrt = new GLenum[m_texCount];
			// attach a texture to FBO color attachement point
			for( GLuint i = 0; i < m_texCount; i++ )
			{
				m_mrt[i] = GL_COLOR_ATTACHMENT0_EXT + i;
				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, GL_TEXTURE_2D, m_tex[i], 0);
			}
		}
		else
		{
			//@ disable color buffer if you don't attach any color buffer image,
			//@ for example, rendering depth buffer only to a texture.
			//@ Otherwise, glCheckFramebufferStatusEXT will not be complete.
			glDrawBuffer(GL_NONE);
			glReadBuffer(GL_NONE);
		}

		if( m_bDepth )
		{
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_texDepth, 0);
		}
		else
		{
			// create a renderbuffer object to store depth info
			// NOTE: A depth renderable image should be attached the FBO for depth test.
			// If we don't attach a depth renderable image to the FBO, then
			// the rendering output will be corrupted because of missing depth test.
			// If you also need stencil test for your rendering, then you must
			// attach additional image to the stencil attachement point, too.
			glGenRenderbuffersEXT(1, &m_rboId);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_rboId);
			glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, m_width, m_height);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
			// attach a renderbuffer to depth attachment point
			glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_rboId);

			//glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_rboId);
		}

		if( m_bStencil )
		{
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, m_texStencil, 0);
		}


		// check FBO status
		printFramebufferInfo();
		bool status = checkFramebufferStatus();
		if(!status)
		{
			DEBUG_ERR("Error crating FBO!\n");
			destroy();
			m_inited = false;
			return;
		}

		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	}

	m_inited = true;
}


///////////////////////////////////////////////////////////////////////////////
// destroy the fbo
///////////////////////////////////////////////////////////////////////////////
void GBuffer::destroy()
{
    if( m_inited )
    {
		if( m_texCount )
		{
			glDeleteTextures(m_texCount, m_tex);
			delete[] m_tex;
			m_tex = NULL;
			if(m_bUseFBO)
			{
				delete[] m_mrt;
				m_mrt = NULL;
			}
		}

		if( m_bUseFBO )
		{
        	glDeleteFramebuffersEXT(1, &m_fboId);
        }
		if( m_bDepth )
		{
			glDeleteTextures(1, &m_texDepth);
		}
		else
		{
			glDeleteRenderbuffersEXT(1, &m_rboId);
		}
		if( m_bStencil )
		{
			glDeleteTextures(1, &m_texStencil);
		}

		m_inited = false;
    }
}


///////////////////////////////////////////////////////////////////////////////
// destroy the fbo
///////////////////////////////////////////////////////////////////////////////
GLuint	GBuffer::getColorTextureId( GLuint texCount )
{
	return m_texCount ? m_tex[ texCount <= m_texCount ? texCount : m_texCount ] : 0;
}


///////////////////////////////////////////////////////////////////////////////
// destroy the fbo
///////////////////////////////////////////////////////////////////////////////
GLuint	GBuffer::getDepthTextureId()
{
	return m_bDepth ? m_texDepth : 0;
}


///////////////////////////////////////////////////////////////////////////////
// destroy the fbo
///////////////////////////////////////////////////////////////////////////////
GLuint	GBuffer::getStencilTextureId()
{
	return m_bStencil ? m_texStencil : 0;
}


///////////////////////////////////////////////////////////////////////////////
// check FBO completeness
///////////////////////////////////////////////////////////////////////////////
bool GBuffer::checkFramebufferStatus()
{
    // check FBO status
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    switch(status)
    {
    case GL_FRAMEBUFFER_COMPLETE_EXT:
        std::cout << "Framebuffer complete." << std::endl;
        return true;

    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
        std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl;
        return false;

    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
        std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl;
        return false;

    case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
        std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl;
        return false;

    case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
        std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl;
        return false;

    case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
        std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl;
        return false;

    case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
        std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl;
        return false;

    case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
        std::cout << "[ERROR] Unsupported by FBO implementation." << std::endl;
        return false;

    default:
        std::cout << "[ERROR] Unknow error." << std::endl;
        return false;
    }
}


///////////////////////////////////////////////////////////////////////////////
// print out the FBO infos
///////////////////////////////////////////////////////////////////////////////
void GBuffer::printFramebufferInfo()
{
    cout << "\n***** FBO STATUS *****\n";

    // print max # of colorbuffers supported by FBO
    int colorBufferCount = 0;
    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &colorBufferCount);
    cout << "Max Number of Color Buffer Attachment Points: " << colorBufferCount << endl;

    int objectType;
    int objectId;

    // print info of the colorbuffer attachable image
    for(int i = 0; i < colorBufferCount; ++i)
    {
        glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT,
                                                 GL_COLOR_ATTACHMENT0_EXT+i,
                                                 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT,
                                                 &objectType);
        if(objectType != GL_NONE)
        {
            glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT,
                                                     GL_COLOR_ATTACHMENT0_EXT+i,
                                                     GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT,
                                                     &objectId);

            std::string formatName;

            cout << "Color Attachment " << i << ": ";
            if(objectType == GL_TEXTURE)
                cout << "GL_TEXTURE, " << getTextureParameters(objectId) << endl;
            else if(objectType == GL_RENDERBUFFER_EXT)
                cout << "GL_RENDERBUFFER_EXT, " << getRenderbufferParameters(objectId) << endl;
        }
    }

    // print info of the depthbuffer attachable image
    glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT,
                                             GL_DEPTH_ATTACHMENT_EXT,
                                             GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT,
                                             &objectType);
    if(objectType != GL_NONE)
    {
        glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT,
                                                 GL_DEPTH_ATTACHMENT_EXT,
                                                 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT,
                                                 &objectId);

        cout << "Depth Attachment: ";
        switch(objectType)
        {
        case GL_TEXTURE:
            cout << "GL_TEXTURE, " << getTextureParameters(objectId) << endl;
            break;
        case GL_RENDERBUFFER_EXT:
            cout << "GL_RENDERBUFFER_EXT, " << getRenderbufferParameters(objectId) << endl;
            break;
        }
    }

    // print info of the stencilbuffer attachable image
    glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT,
                                             GL_STENCIL_ATTACHMENT_EXT,
                                             GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT,
                                             &objectType);
    if(objectType != GL_NONE)
    {
        glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER_EXT,
                                                 GL_STENCIL_ATTACHMENT_EXT,
                                                 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT,
                                                 &objectId);

        cout << "Stencil Attachment: ";
        switch(objectType)
        {
        case GL_TEXTURE:
            cout << "GL_TEXTURE, " << getTextureParameters(objectId) << endl;
            break;
        case GL_RENDERBUFFER_EXT:
            cout << "GL_RENDERBUFFER_EXT, " << getRenderbufferParameters(objectId) << endl;
            break;
        }
    }

    cout << endl;
}


///////////////////////////////////////////////////////////////////////////////
// return renderbuffer parameters as string using glGetRenderbufferParameterivEXT
///////////////////////////////////////////////////////////////////////////////
std::string GBuffer::getRenderbufferParameters(GLuint id)
{
    if(glIsRenderbufferEXT(id) == GL_FALSE)
        return "Not Renderbuffer object";

    int width, height, format;
    std::string formatName;
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
    glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_WIDTH_EXT, &width);    // get renderbuffer width
    glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_HEIGHT_EXT, &height);  // get renderbuffer height
    glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_INTERNAL_FORMAT_EXT, &format); // get renderbuffer internal format
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

    formatName = convertInternalFormatToString(format);

    std::stringstream ss;
    ss << width << "x" << height << ", " << formatName;
    return ss.str();
}


///////////////////////////////////////////////////////////////////////////////
// convert OpenGL internal format enum to string
///////////////////////////////////////////////////////////////////////////////
std::string GBuffer::convertInternalFormatToString(GLenum format)
{
    std::string formatName;

    switch(format)
    {
    case GL_STENCIL_INDEX:
        formatName = "GL_STENCIL_INDEX";
        break;
    case GL_DEPTH_COMPONENT:
        formatName = "GL_DEPTH_COMPONENT";
        break;
    case GL_DEPTH_COMPONENT16:
        formatName = "GL_DEPTH_COMPONENT16";
        break;
    case GL_DEPTH_COMPONENT24:
        formatName = "GL_DEPTH_COMPONENT24";
        break;
    case GL_DEPTH_COMPONENT32:
        formatName = "GL_DEPTH_COMPONENT32";
        break;
    case GL_ALPHA:
        formatName = "GL_ALPHA";
        break;
    case GL_RGB:
        formatName = "GL_RGB";
        break;
    case GL_RGBA:
        formatName = "GL_RGBA";
        break;
    case GL_LUMINANCE:
        formatName = "GL_LUMINANCE";
        break;
    case GL_LUMINANCE_ALPHA:
        formatName = "GL_LUMINANCE_ALPHA";
        break;
    case GL_ALPHA4:
        formatName = "GL_ALPHA4";
        break;
    case GL_ALPHA8:
        formatName = "GL_ALPHA8";
        break;
    case GL_ALPHA12:
        formatName = "GL_ALPHA12";
        break;
    case GL_ALPHA16:
        formatName = "GL_ALPHA16";
        break;
    case GL_LUMINANCE4:
        formatName = "GL_LUMINANCE4";
        break;
    case GL_LUMINANCE8:
        formatName = "GL_LUMINANCE8";
        break;
    case GL_LUMINANCE12:
        formatName = "GL_LUMINANCE12";
        break;
    case GL_LUMINANCE16:
        formatName = "GL_LUMINANCE16";
        break;
    case GL_LUMINANCE4_ALPHA4:
        formatName = "GL_LUMINANCE4_ALPHA4";
        break;
    case GL_LUMINANCE6_ALPHA2:
        formatName = "GL_LUMINANCE6_ALPHA2";
        break;
    case GL_LUMINANCE8_ALPHA8:
        formatName = "GL_LUMINANCE8_ALPHA8";
        break;
    case GL_LUMINANCE12_ALPHA4:
        formatName = "GL_LUMINANCE12_ALPHA4";
        break;
    case GL_LUMINANCE12_ALPHA12:
        formatName = "GL_LUMINANCE12_ALPHA12";
        break;
    case GL_LUMINANCE16_ALPHA16:
        formatName = "GL_LUMINANCE16_ALPHA16";
        break;
    case GL_INTENSITY:
        formatName = "GL_INTENSITY";
        break;
    case GL_INTENSITY4:
        formatName = "GL_INTENSITY4";
        break;
    case GL_INTENSITY8:
        formatName = "GL_INTENSITY8";
        break;
    case GL_INTENSITY12:
        formatName = "GL_INTENSITY12";
        break;
    case GL_INTENSITY16:
        formatName = "GL_INTENSITY16";
        break;
    case GL_R3_G3_B2:
        formatName = "GL_R3_G3_B2";
        break;
    case GL_RGB4:
        formatName = "GL_RGB4";
        break;
    case GL_RGB5:

        formatName = "GL_RGB4";
        break;
    case GL_RGB8:
        formatName = "GL_RGB8";
        break;
    case GL_RGB10:
        formatName = "GL_RGB10";
        break;
    case GL_RGB12:
        formatName = "GL_RGB12";
        break;
    case GL_RGB16:
        formatName = "GL_RGB16";
        break;
    case GL_RGBA2:
        formatName = "GL_RGBA2";
        break;
    case GL_RGBA4:
        formatName = "GL_RGBA4";
        break;
    case GL_RGB5_A1:
        formatName = "GL_RGB5_A1";
        break;
    case GL_RGBA8:
        formatName = "GL_RGBA8";
        break;
    case GL_RGB10_A2:
        formatName = "GL_RGB10_A2";
        break;
    case GL_RGBA12:
        formatName = "GL_RGBA12";
        break;
    case GL_RGBA16:
        formatName = "GL_RGBA16";
        break;
    default:
        formatName = "Unknown Format";
    }

    return formatName;
}


///////////////////////////////////////////////////////////////////////////////
// return texture parameters as string using glGetTexLevelParameteriv()
///////////////////////////////////////////////////////////////////////////////
std::string GBuffer::getTextureParameters(GLuint id)
{
    if(glIsTexture(id) == GL_FALSE)
        return "Not texture object";

    int width, height, format;
    std::string formatName;
    glBindTexture(GL_TEXTURE_2D, id);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);            // get texture width
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);          // get texture height
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); // get texture internal format
    glBindTexture(GL_TEXTURE_2D, 0);

    formatName = convertInternalFormatToString(format);

    std::stringstream ss;
    ss << width << "x" << height << ", " << formatName;
    return ss.str();
}


///////////////////////////////////////////////////////////////////////////////
// generate mip maps
///////////////////////////////////////////////////////////////////////////////
void GBuffer::generateMipMaps()
{
	if( m_texCount || m_bDepth || m_bStencil)
	{
		for( GLuint i = 0; i < m_texCount; i++ )
		{
			glBindTexture(GL_TEXTURE_2D, m_tex[i]);
			glGenerateMipmapEXT(GL_TEXTURE_2D);
		}

		if( m_bDepth )
		{
			glBindTexture(GL_TEXTURE_2D, m_texDepth);
			glGenerateMipmapEXT(GL_TEXTURE_2D);
		}

		if( m_bStencil )
		{
			glBindTexture(GL_TEXTURE_2D, m_texStencil);
			glGenerateMipmapEXT(GL_TEXTURE_2D);
		}

#ifndef	FAST
		glBindTexture(GL_TEXTURE_2D, 0);
#endif
	}
}


///////////////////////////////////////////////////////////////////////////////
// begin rendering to buffer
///////////////////////////////////////////////////////////////////////////////
void GBuffer::begin()
{
	ASSERT( m_inited );

	if( m_bUseFBO )
	{

		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId);

		GLbitfield clearMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
		if( m_bStencil )
		{
			clearMask |= GL_STENCIL_BUFFER_BIT;
		}
		glClear(clearMask);

		if( m_texCount )
		{
			glDrawBuffers(m_texCount, m_mrt);
		}

	}
	else
	{

		GLbitfield clearMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
		if( m_bStencil )
		{
			clearMask |= GL_STENCIL_BUFFER_BIT;
		}
		glClear(clearMask);

		glPushAttrib(GL_COLOR_BUFFER_BIT | GL_PIXEL_MODE_BIT);
        glDrawBuffer(GL_BACK);
        glReadBuffer(GL_BACK);

	}

	if( m_bStencil )
	{
		glEnable(GL_STENCIL_TEST);
	}
	else
	{
		glDisable(GL_STENCIL_TEST);
	}
}


///////////////////////////////////////////////////////////////////////////////
// end rendering to buffer
///////////////////////////////////////////////////////////////////////////////
void GBuffer::end(GLuint ti)
{
	ASSERT( m_inited );

	if( m_bUseFBO )
	{
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	}
	else
	{
		if( m_texCount && ti < m_texCount )
		{
			glBindTexture(GL_TEXTURE_2D, m_tex[ti]);
		}
		else if( m_bDepth && ti == m_texCount )
		{
			glBindTexture(GL_TEXTURE_2D, m_texDepth);
		}
		else if( m_bStencil && ti == m_texCount + 1 )
		{
			glBindTexture(GL_TEXTURE_2D, m_texStencil);
		}
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height);
#ifndef	FAST
        glBindTexture(GL_TEXTURE_2D, 0);
#endif
		glPopAttrib();
	}

#ifndef	FAST
	if( m_bStencil )
	{
		glDisable(GL_STENCIL_TEST);
	}
#endif
}


///////////////////////////////////////////////////////////////////////////////
// bind captured textures to use
///////////////////////////////////////////////////////////////////////////////
void GBuffer::bindTextures()
{
	ASSERT( m_inited );

	GLuint i = 0;
	
	if( m_bDepth )
	{
		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, m_texDepth);
		i++;
	}

	if( m_bStencil )
	{
		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, m_texStencil);
		i++;
	}

	if( m_texCount )
	{
		for( GLuint t = 0; t < m_texCount; t++ )
		{
			glActiveTexture(GL_TEXTURE0 + i);
			glBindTexture(GL_TEXTURE_2D, m_tex[t]);
			i++;
		}
	}

}


///////////////////////////////////////////////////////////////////////////////
// bind texture
///////////////////////////////////////////////////////////////////////////////
void GBuffer::bindTexture(GLenum texTarget, GLuint texIndex)
{
	if( texIndex < m_texCount )
	{
		glActiveTexture(texTarget);
		glBindTexture(GL_TEXTURE_2D, m_tex[texIndex]);
	}
}


///////////////////////////////////////////////////////////////////////////////
// bind texture
///////////////////////////////////////////////////////////////////////////////
void GBuffer::bindDepthTexture(GLenum texTarget)
{
	if( m_bDepth )
	{
		glActiveTexture(texTarget);
		glBindTexture(GL_TEXTURE_2D, m_texDepth);
	}
}


///////////////////////////////////////////////////////////////////////////////
// bind texture
///////////////////////////////////////////////////////////////////////////////
void GBuffer::bindStencilTexture(GLenum texTarget)
{
	if( m_bStencil )
	{
		glActiveTexture(texTarget);
		glBindTexture(GL_TEXTURE_2D, m_texStencil);
	}
}

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
